package com.qcrud.core.result;

import com.qcrud.core.SqlData;
import com.qcrud.core.paging.Paging;
import com.qcrud.core.type.DataType;
import com.qcrud.tookit.BeanMapUtils;
import com.qcrud.tookit.ClassUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

public record QueryResult(List<Map<String, Object>> dataList) {
    private static final Map<String, ReturnInfo> RETURN_INFO_MAP = new ConcurrentHashMap<>();

    public Object mapTo(Type resultType) {
        return mapTo(null, resultType);
    }

    public Object mapTo(SqlData sqlData, Type resultType) {
        if (null == dataList || dataList.isEmpty()) {
            return null;
        }
        ReturnInfo returnInfo;
        if (null == sqlData) {
            returnInfo = this.getReturnInfo(null, resultType);
        } else {
            returnInfo = RETURN_INFO_MAP.computeIfAbsent(sqlData.getStatementKey(),
                k -> this.getReturnInfo(sqlData.getEntityClass(), resultType));
        }
        Object result = null;
        int type = returnInfo.type();
        Class beanClazz = returnInfo.beanClass();
        if (0 == type) {
            // List, Page
            result = BeanMapUtils.mapsToBeans(dataList, beanClazz);
        } else if (1 == type) {
            // Map
            result = dataList;
        } else if (2 == type) {
            // Entity
            result = BeanMapUtils.mapToBean(dataList.get(0), beanClazz);
        } else {
            // Basic type
            for (Map<String, Object> dl : dataList) {
                for (Map.Entry<String, Object> entry : dl.entrySet()) {
                    result = entry.getValue();
                    break;
                }
                if (null != result) {
                    break;
                }
            }
        }
        return returnInfo.isOptional() ? Optional.of(result) : result;
    }

    protected ReturnInfo getReturnInfo(Class entityClass, Type resultType) {
        int _type = -1;
        Type rawType = resultType;
        if (resultType instanceof ParameterizedType) {
            rawType = ((ParameterizedType) resultType).getRawType();
        }
        boolean isOptional = false;
        Class beanClazz = null;
        if (rawType == Optional.class) {
            isOptional = true;
            Type[] actualTypeArguments = ClassUtils.getActualTypeArguments(resultType);
            if (null != actualTypeArguments) {
                Type t0 = actualTypeArguments[0];
                if ("T".equals(t0.getTypeName())) {
                    // 泛型无法获取情况从 SqlData 中读取
                    beanClazz = entityClass;
                    if (rawType != List.class) {
                        rawType = beanClazz;
                    }
                } else if (t0 instanceof ParameterizedType) {
                    ParameterizedType parameterizedType = (ParameterizedType) t0;
                    rawType = parameterizedType.getRawType();
                    Type[] ats = parameterizedType.getActualTypeArguments();
                    if (null != ats) {
                        Type at0 = ats[0];
                        if ("T".equals(at0.getTypeName())) {
                            beanClazz = entityClass;
                        } else {
                            beanClazz = ClassUtils.getClass(at0);
                        }
                    }
                }
            }
        }
        if (rawType == List.class || rawType == Paging.class) {
            if (!isOptional) {
                // 非返回 Optional 情况
                Type type = ClassUtils.getActualTypeArguments(resultType)[0];
                beanClazz = ClassUtils.getClass(type);
            }
            _type = 0;
        } else if (rawType == Map.class) {
            _type = 1;
        } else {
            Class rawClz = ClassUtils.getClass(rawType);
            if (null == DataType.of(rawClz)) {
                // 实体对象
                if (null == beanClazz) {
                    beanClazz = rawClz;
                }
                _type = 2;
            }
        }
        return new ReturnInfo(_type, isOptional, beanClazz);
    }
}
